const http = require('http');
// 引入对应的对象，这些对象的属性在对应的文件中以及处理了
// 然后在这里只需要在合适的时候添加在ctx对象上面就行了
const context = require("./context.js");
const request = require("./request.js");
const response = require("./response.js");

class Application {
  // 初始化函数
  constructor() {
    this.middlewares = [];
  }

  // 监听函数
  listen(...args) {
    // 使用原生的 node http 模块创建服务
    const server = http.createServer(async (req, res) => {
      // 创建上下文对象
      const ctx = this.createContext(req, res);

      // 组合中间件
      const fn = this.composeAsync(this.middlewares);

      await fn(ctx);

      // 响应数据
      res.end(ctx.body);
    })
    server.listen(...args);
  }

  // 组合中间件
  composeAsync(mids) {
    return (ctx) => {
      let dispatch = (value) => {
        let fn = mids[value];
        if (!fn) { // 如果执行到了最后一个函数直接返回
          return Promise.resolve();
        }
        let next = () => {
          dispatch(value + 1)
        }
        return Promise.resolve(fn(ctx, next));
      }

      // 自动调用第一个函数
      return dispatch(0);
      // 这里是自动调用了第一个函数，这里最好是一步一步往下走，这样可以更好的理解流程
      // 这里还需要注意的是，这个由于是一个实验测试的 compose 函数
      // 所以函数的第一个参数就直接是这个 next 函数了，这个是比较容易产生问题的地方
    }
  }

  // 创建上下文
  createContext(req, res) {
    // 创建上下文对象
    // 并且给上下文添加 request 和 response 对象
    const ctx = Object.create(context);
    ctx.request = Object.create(request);
    ctx.response = Object.create(response);

    // 将传入的 req 和 res 赋值给上下文对应的属性
    ctx.req = req;
    ctx.request.req = req;

    ctx.res = res;
    ctx.response.res = res;

    // 返回上下文对象
    return ctx;
  }

  // 注册中间件
  use(middlewares) {
    this.middlewares.push(middlewares);
  }
}

module.exports = Application;
